package cn.wn.shiro;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * Shiro的配置类：
 */
@Configuration
public class ShiroConfig {
    /**
     * 4、创建md5加密对象
     */
    @Bean
    public CredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher
                = new HashedCredentialsMatcher();
        // 指定加密算法：md5、sha256、sha512
        credentialsMatcher.setHashAlgorithmName("md5");
        // 指定迭代次数，默认也是1
        credentialsMatcher.setHashIterations(1);
        return credentialsMatcher;
    }

    /**
     * 1、创建Shiro对象，加入容器
     * 注入加密对象，自动对用户输入的密码按照md5加密
     */
    @Bean
    public ShiroRealm shiroRealm(
            CredentialsMatcher credentialsMatcher){
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(credentialsMatcher);
        return shiroRealm;
    }

    /**
     * 2、创建SecurityManager对象，通过方法参数注入realm
     * @Bean注解
     * A. 自动把方法返回的对象加入容器
     * B. 对于方法的参数，会自动去容器中找参数类型对应的对象注入。
     *    如果该方法参数的类型对应容器中有多个对象，按照形参名称去容器中找对应的对象注入。
     */
    @Bean
    public SecurityManager securityManager(ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager =
                new DefaultWebSecurityManager();
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    /**
     * 3、创建Shiro的过滤器工厂，指定哪些资源应用哪些过滤器
     * anon    匿名访问过滤器，指定放行的资源
     * authc   认证过滤器，指定认证拦截的资源
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        //3.1 创建shiro过滤器工厂
        ShiroFilterFactoryBean factoryBean =
                new ShiroFilterFactoryBean();
        //3.2 设置shiro的安全管理器对象
        factoryBean.setSecurityManager(securityManager);
        //3.3 设置其他参数: setLoginUrl 表示指定登陆页面路径；认证失败对应的页面
        factoryBean.setLoginUrl("/login.html");
        //3.3 设置其他参数: setUnauthorizedUrl 指定授权校验失败对应的页面
        factoryBean.setUnauthorizedUrl("/unauthorizedUrl.html");

        //3.4 通过shiro的过滤器链，作用：指定放行哪些资源、拦截哪些资源
        Map<String,String> map = new HashMap<>();
        //参数1：拦截的资源； 参数2：使用的过滤器
        map.put("/css/**","anon");
        map.put("/img/**","anon");
        map.put("/js/**","anon");
        map.put("/pages/**","anon");
        map.put("/plugins/**","anon");
        map.put("/test/**","anon");
        map.put("/login/**","anon");
        map.put("/login","anon");
        map.put("/login.html*","anon");

        map.put("/**","authc");
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }

    /**
     * 5、开启Shiro权限注解支持
     */
    //5.1 开启Aop自动代理
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator =
                new DefaultAdvisorAutoProxyCreator();
        // 默认为false，会自动选择代理方式。true设置代理方式为子类代理。（可选）
        // 因为控制器没有实现接口
        creator.setProxyTargetClass(true);
        return creator;
    }
    //5.2 匹配所有加了授权注解的方法，需要注入SecurityManager
    @Bean
    public AuthorizationAttributeSourceAdvisor
                authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor =
                new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
